package com.msteg.b4a.app;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.witness.informacam.InformaCam;
import org.witness.informacam.models.forms.IForm;
import org.witness.informacam.models.j3m.IData;
import org.witness.informacam.models.j3m.IRegionData;
import org.witness.informacam.models.media.ILog;
import org.witness.informacam.models.media.IMedia;
import org.witness.informacam.storage.FormUtility;
import org.witness.informacam.ui.CameraActivity;
import org.witness.informacam.utils.Constants.App;
import org.witness.informacam.utils.Constants.Models;
import org.witness.informacam.utils.Constants.App.Storage.Type;
import org.witness.informacam.utils.Constants.Models.IMedia.MimeType;
import org.witness.informacam.utils.InformaCamBroadcaster.InformaCamStatusListener;

import com.msteg.b4a.Constants.Codes;
import com.msteg.b4a.Constants.Form;
import com.msteg.b4a.Constants.MainActivityListener;
import com.msteg.b4a.R;
import com.msteg.b4a.app.adapters.ObservationListAdapter;
import com.msteg.b4a.app.popups.WaitPopup;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity  extends Activity implements OnClickListener, InformaCamStatusListener, MainActivityListener {
	LinearLayout newObservationHolder, newObservation;
	ExpandableListView observationList;
	TextView noObservations, currentObservationMediaCount;
	ProgressBar waiter;

	ILog currentObservation = null;
	EditText currentObservationContent;
	Button newObservationCommit;
	ImageButton toCamera;

	InformaCam informaCam;
	
	Handler h = new Handler();

	private final static String LOG = App.LOG;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.activity_main);
	}

	private void initLayout() {		
		newObservation = (LinearLayout) findViewById(R.id.main_new_observation);
		newObservation.setOnClickListener(this);
		newObservation.setClickable(false);

		newObservationHolder = (LinearLayout) findViewById(R.id.new_observation_holder);

		toCamera = (ImageButton) findViewById(R.id.new_observation_to_camera);
		toCamera.setOnClickListener(this);

		newObservationCommit = (Button) findViewById(R.id.new_observation_commit);
		newObservationCommit.setOnClickListener(this);

		noObservations = (TextView) findViewById(R.id.main_no_observations);
		observationList = (ExpandableListView) findViewById(R.id.main_observation_list);
		h.post(new Runnable() {

			@Override
			public void run() {
				observationList.setIndicatorBounds(observationList.getRight()- 40, observationList.getWidth());
			}
		});

		currentObservationContent = (EditText) findViewById(R.id.new_observation_content);
		currentObservationMediaCount = (TextView) findViewById(R.id.new_observation_media_count);

		waiter = (ProgressBar) findViewById(R.id.main_waiter);
	}

	@Override
	public void onResume() {
		super.onResume();

		Log.d(LOG, "on resume called.");

		informaCam = InformaCam.getInstance(this);
		if(informaCam.informaService == null) {
			informaCam.startInforma();
		}

		/*
		informaCam.mediaManifest.media.clear();
		informaCam.mediaManifest.save();
		 */

		initLayout();
	}

	@SuppressWarnings("unchecked")
	private void refreshObservations() {
		Log.d(LOG, "CALLING REFRESHER");
		informaCam.mediaManifest.sortBy(Models.IMediaManifest.Sort.DATE_ASC);
		List<ILog> observations = informaCam.mediaManifest.getAllByType(MimeType.LOG);

		if(observations == null) {
			observationList.setVisibility(View.GONE);
			noObservations.setVisibility(View.VISIBLE);
			return;
		}

		noObservations.setVisibility(View.GONE);
		observationList.setVisibility(View.VISIBLE);
		observationList.setAdapter(new ObservationListAdapter(observations));

	}

	private void initObservation(boolean isNew) {
		currentObservationContent.setText("");
		byte[] answerBytes = null;

		if(isNew) {
			currentObservation = new ILog();
			try {
				currentObservation.put(Models.IMedia.Flags.IS_NEW, true);
			} catch (JSONException e) {
				Log.e(LOG, e.toString());
				e.printStackTrace();
			}

			currentObservation.startTime = informaCam.informaService.getCurrentTime();
			currentObservation._id = currentObservation.generateId("log_" + System.currentTimeMillis());

			info.guardianproject.iocipher.File rootFolder = new info.guardianproject.iocipher.File(currentObservation._id);
			if(!rootFolder.exists()) {
				rootFolder.mkdir();
			}
			currentObservation.rootFolder = rootFolder.getAbsolutePath();

			info.guardianproject.iocipher.File formContent = new info.guardianproject.iocipher.File(rootFolder, "form");
			currentObservation.formPath = formContent.getAbsolutePath();
		} else {
			answerBytes = informaCam.ioService.getBytes(currentObservation.formPath, Type.IOCIPHER);
		}

		currentObservation.attachedForm = new IForm(FormUtility.getAvailableForms().get(0), this, answerBytes);
		currentObservation.attachedForm.associate(currentObservationContent, Form.CONTENT);
		informaCam.informaService.associateMedia(currentObservation);

		updateMediaCount();
		showFormHolder();
	}

	private void updateMediaCount() {
		currentObservationMediaCount.setText(getString(R.string.media_x, currentObservation.attachedMedia.size()));
	}

	private void showFormHolder() {
		// TODO: SCROLL TO TOP!

		newObservationHolder.setVisibility(View.VISIBLE);
		newObservation.setClickable(false);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onPrepareOptionsMenu(menu);
		MenuInflater mi = getMenuInflater();
		mi.inflate(R.menu.menu, menu);
		return true;
	}

	private void exportDeviceCredentials() {
		java.io.File credentials = informaCam.ioService.getPublicCredentials();
		Intent intent = new Intent()
			.setAction(Intent.ACTION_SEND)
			.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(credentials))
			.setType("file/");

		startActivity(Intent.createChooser(intent, getString(R.string.send)));

	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch(item.getItemId()) {
		case R.id.b4a_export_all_to_sd_card:
			burnObservations();
			
			break;
		case R.id.b4a_export_all:
			Toast.makeText(this, "feature coming soon\u2026", Toast.LENGTH_LONG).show();
			// new WaitPopup(this);
			break;
		case R.id.b4a_export_device_credentials:
			exportDeviceCredentials();
			break;
		case R.id.b4a_light_screen_dark_screen:
			Toast.makeText(this, "feature coming soon\u2026", Toast.LENGTH_LONG).show();
			break;
		case R.id.b4a_logout:
			informaCam.mediaManifest.save();
			setResult(Activity.RESULT_OK, new Intent().putExtra(Codes.Extras.LOGOUT_USER, true));
			finish();
			
			break;
		}

		return super.onOptionsItemSelected(item);
	}
	
	private void burnObservations() {
		informaCam.informaService.flushCache();
		
		final WaitPopup ws = new WaitPopup(this, informaCam.mediaManifest.getAllByType(MimeType.LOG));
		h.postDelayed(new Runnable() {
			@Override
			public void run() {
				ws.init();
			}
		}, 300);
		
		
	}

	private void saveObservation() {
		currentObservation.endTime = informaCam.informaService.getCurrentTime();
		currentObservation.attachedForm.answer(Form.CONTENT);		
		
		try {
			info.guardianproject.iocipher.FileOutputStream fos = new info.guardianproject.iocipher.FileOutputStream(currentObservation.formPath);

			if(currentObservation.attachedForm.save(fos) != null) {
				if(currentObservation.data == null) {
					currentObservation.data = new IData();
					currentObservation.data.regionData = new ArrayList<IRegionData>();
				}
				
				IRegionData regionData = new IRegionData(currentObservation.attachedForm, currentObservation.formPath);
				regionData.metadata.put(Models.IMedia.ILog.START_TIME, currentObservation.startTime);
				regionData.metadata.put(Models.IMedia.ILog.END_TIME, currentObservation.endTime);
				regionData.timestamp = informaCam.informaService.getCurrentTime();
				currentObservation.data.regionData.add(regionData);
				
				if(informaCam.mediaManifest.media == null) {
					informaCam.mediaManifest.media = new ArrayList<IMedia>();
				}

				if(currentObservation.has(Models.IMedia.Flags.IS_NEW) && currentObservation.getBoolean(Models.IMedia.Flags.IS_NEW)) {
					informaCam.mediaManifest.media.add(currentObservation);
					currentObservation.remove(Models.IMedia.Flags.IS_NEW);
					
					informaCam.mediaManifest.save();
				} else {
					//informaCam.mediaManifest.getById(currentObservation._id).inflate(currentObservation.asJson());
					currentObservation.save();
				}

				
				refreshObservations();
			}
		} catch (FileNotFoundException e) {
			Log.e(LOG, e.toString());
			e.printStackTrace();
		} catch (JSONException e) {
			Log.e(LOG, e.toString());
			e.printStackTrace();
		}
	}

	private void toggleObservationManager() {
		toggleObservationManager(true);
	}

	private void toggleObservationManager(boolean isNew) {
		if(currentObservation == null) {
			initObservation(isNew);
			showFormHolder();

		} else {
			newObservationHolder.setVisibility(View.GONE);
			newObservation.setClickable(true);

			saveObservation();
			currentObservation = null;
		}
	}

	@Override
	public void onClick(View v) {
		if(v == newObservation) {
			toggleObservationManager();
		} else if(v == newObservationCommit) {
			toggleObservationManager();
		} else if(v == toCamera) {
			startActivityForResult(new Intent(this, CameraActivity.class), Codes.Routes.CAMERA);
		}

	}

	@Override
	public void onBackPressed() {
		informaCam.mediaManifest.save();
		informaCam.stopInforma();
	}

	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		if(resultCode == Activity.RESULT_OK) {
			switch(requestCode) {
			case Codes.Routes.CAMERA:
				// associate media to the current observation
				try {
					JSONArray newMedia = ((JSONObject) new JSONTokener(data.getStringExtra(Codes.Extras.RETURNED_MEDIA)).nextValue()).getJSONArray("dcimEntries");
					for(int n=0; n<newMedia.length(); n++) {
						JSONObject mJson = newMedia.getJSONObject(n);
						currentObservation.attachedMedia.add(mJson.getString("_id"));
					}
					updateMediaCount();

				} catch (JSONException e) {
					Log.e(LOG, e.toString());
					e.printStackTrace();
				}

				refreshObservations();
				break;
			}
		}

	}

	@Override
	public void onInformaCamStart(Intent intent) {}

	@Override
	public void onInformaCamStop(Intent intent) {}

	@Override
	public void onInformaStop(Intent intent) {
		setResult(Activity.RESULT_CANCELED);
		finish();
	}

	@Override
	public void onInformaStart(Intent intent) {
		waiter.setVisibility(View.GONE);
		newObservation.setClickable(true);
		refreshObservations();
	}

	@Override
	public void addMedia(ILog observation) {
		startActivityForResult(new Intent(this, CameraActivity.class), Codes.Routes.CAMERA);
	}

	@Override
	public void editLog(ILog observation) {
		currentObservation = observation;
		initObservation(false);

	}

	@Override
	public void deleteLog(ILog observation) {
		for(String s : observation.attachedMedia) {
			informaCam.mediaManifest.getById(s).delete();
		}
		
		informaCam.mediaManifest.getById(observation._id).delete();
		refreshObservations();
	}


}